package edu.uestc.gene.scripts.combination;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import edu.uestc.gene.entity.AdverseDrugReaction;
import edu.uestc.gene.entity.Drug;
import edu.uestc.gene.entity.Gene;
import edu.uestc.gene.model.Statistics;
import edu.uestc.gene.model.Tool;
import edu.uestc.gene.scripts.combination.entity.CombinedGene;
import edu.uestc.gene.scripts.combination.entity.Frequencies;
import edu.uestc.gene.scripts.combination.entity.Group;

/**
 * @author Carl
 * 
 *         2013-10-29
 */
public class DataTesting {

	private Statistics statistics;

	/**
	 * 
	 */
	public DataTesting() {
		statistics = Statistics.getInstance();
		statistics.initData(false);
	}

	/**
	 * 统计Gene和ADR之间的关系
	 * 
	 * @param count
	 * @param threshold1
	 * @param threshold2
	 * @return 
	 *         {总ADR数，对应基因不多于threshold1的ADR数，对应基因不多于threshold2的ADR数，对应基因最小值，对应基因最大值
	 *         ，对应基因平均值，对应基因标准差}
	 */
	private double[] statiticsCount(int[] count, int threshold1, int threshold2) {
		int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE, filted_1 = 0, filted_2 = 0;
		count = Tool.shellSort(count);// 将数据排序以便制作折线图
		for (int i = 0; i < count.length; i++) {
			System.out.print(count[i] + "\t");// 制表符间隔数据可以直接粘贴数据到Excel中制表
			if (count[i] <= threshold1)
				filted_1++;
			if (count[i] <= threshold2)
				filted_2++;
			if (count[i] < min)
				min = count[i];
			if (count[i] > max)
				max = count[i];
		}
		return new double[] { count.length, filted_1, filted_2, min, max,
				Tool.getAverage(count), Tool.getStandardDevition(count) };
	}

	/**
	 * 获取Gene和ADR之间关系
	 * 
	 * @return
	 */
	private Group getGARelation() {
		List<AdverseDrugReaction> adrs = statistics.getAdrs();
		Group group = new Group();
		List<Frequencies> frequencies = new ArrayList<Frequencies>();
		for (int i = 0; i < adrs.size(); i++) {
			Frequencies freq = new Frequencies();
			freq.setAdr(adrs.get(i));
			List<Gene> adr2Genes = new ArrayList<Gene>();// 每个ADR对应的基因
			List<Drug> drugs = adrs.get(i).getDrugs();
			for (int j = 0; j < drugs.size(); j++) {
				List<Gene> genes = drugs.get(j).getDrug_genes();
				for (int k = 0; k < genes.size(); k++) {
					if (!adr2Genes.contains(genes.get(k)))
						adr2Genes.add(genes.get(k));
				}
			}
			// freq.addGenes(adr2Genes);
			freq.setGroup(adr2Genes);
			frequencies.add(freq);
			// System.out
			// .println("副作用" + i + ":" + adrs.get(i).getName() + "统计完成");
		}
		group.setFreq(frequencies);
		return group;
	}

	/**
	 * 统计Gene和ADR之间的关系
	 * 
	 * 
	 * @return 
	 *         {总ADR数，对应基因不多于threshold1的ADR数，对应基因不多于threshold2的ADR数，对应基因最小值，对应基因最大值
	 *         ，对应基因平均值，对应基因标准差}
	 */
	public double[] calculateGARelation() {
		List<Frequencies> freq = getGARelation().getFreq();
		System.out.println("获取GA关系完成");
		Map<String, Integer> adrs_encode = statistics.getAdrs_encode();
		int[] count = new int[Statistics.ADRS_NUM];
		for (int i = 0; i < freq.size(); i++) {
			List<Gene> adr2genes = freq.get(i).getGroup();
			for (int j = 0; j < adr2genes.size(); j++) {
				count[adrs_encode.get(freq.get(i).getAdr().getName())]++;
			}
		}
		System.out.println("频率统计完成");
		return statiticsCount(count, 40, 100);// 以40和100为阈值进行统计（每个副作用对应的基因个数）
	}

	/**
	 * 将各个副作用对应各组基因综合到一起按照出现频率分组<br>
	 * 因为在每一个合并的基因组里面的所有基因的出现频率是肯定相同的
	 * 
	 * @param statistics
	 * @return
	 */
	private List<List<Gene>> getFrequencyOfOccurrence(Statistics statistics,
			List<Frequencies> groups) {
		Map<String, Integer> gene_encode = statistics.getGenes_encode();
		List<Gene> genes = statistics.getGenes();
		List<List<Gene>> freq = new ArrayList<List<Gene>>();
		int[] count = new int[Statistics.GENES_NUM];
		for (int i = 0; i < groups.size(); i++) {
			List<Gene> adr2genes = groups.get(i).getGroup();
			for (int j = 0; j < adr2genes.size(); j++) {
				count[gene_encode.get(adr2genes.get(j).getName())]++;
			}
		}
		for (int i = 0; i < count.length; i++) {
			if (count[i] == -1) {
				continue;
			}
			List<Gene> geneGroup = new ArrayList<Gene>();
			geneGroup.add(genes.get(i));
			for (int j = i + 1; j < count.length; j++) {
				if (count[i] == count[j]) {
					geneGroup.add(genes.get(j));
					count[j] = -1;
				}
			}
			if (geneGroup.size() != 1) {
				freq.add(geneGroup);
			}
		}
		return freq;
	}

	/**
	 * 统计合并Gene之后，Gene和ADR之间关系数量
	 * 
	 * @return {总ADR数，对应基因不多于40的ADR数，对应基因最小值，对应基因最大值，对应基因平均值，对应基因标准差}
	 */
	private List<CombinedGene> getCombinedGARalation(Group groups) {
		// 首先根据ADR和Gene对应关系获得每个gene出现的频率，并将频率相同的Gene分为一组
		List<List<Gene>> freq = getFrequencyOfOccurrence(statistics,
				groups.getFreq());
		List<Integer> filter = new ArrayList<Integer>();
		for (int i = 0; i < freq.size(); i++) {
			if (freq.get(i).size() > 100) {
				filter.add(freq.get(i).size());
			}
		}
		// System.out.println("一共有" + freq.size() +
		// "组相同频率的基因组，其中基因数量超过100的数量如下：");
		// for (int i = 0; i < filter.size(); i++) {
		// System.out.print(filter.get(i) + " ");
		// }
		// System.out.println();
		List<CombinedGene> combines = new ArrayList<CombinedGene>();// 最后产生的合并基因集合
		for (int i = 0; i < freq.size(); i++) {
			List<Gene> equalItems = freq.get(i);
			System.out.print("第" + (i + 1) + "组，共有" + equalItems.size()
					+ "个出现频率相同的基因");
			StringBuffer sb = new StringBuffer();
			for (int j = 0; j < equalItems.size(); j++) {
				sb.append(equalItems.get(j).getName() + ",");
			}
			System.out.println("(" + sb.toString() + ")");
			if (equalItems.size() > 100) {
				System.out.println("\t当前基因组内容较多，显示详细内容：");
			}
			int firstIndex = 0;
			while (firstIndex < equalItems.size() - 1) {
				if (firstIndex % 20 == 0 && equalItems.size() > 100) {
					System.out.println("\t当前已经处理到第" + firstIndex + "个基因了");
				}
				Gene current = equalItems.get(firstIndex);
				firstIndex++;
				// 如果当前基因已经被入选到某一个合并基因组里面则直接跳过该基因
				if (current.isCombined()) {
					continue;
				}
				CombinedGene combinedGene = new CombinedGene("CombinedGene"
						+ (i + 1));
				combinedGene.addGene(current);
				current.setCombined(true);// 首先将第一个默认为可以合并，如果不能合并再修改回来
				List<Frequencies> filtedFrequencies = groups
						.findFrequenciesByGene(current);
				for (int j = firstIndex; j < equalItems.size(); j++) {
					Gene candidate = equalItems.get(j);
					// 如果候选基因已经被入选到合并基因组列则跳过该候选基因
					if (candidate.isCombined()) {
						continue;
					}
					List<Frequencies> afterFiltedFrequencies = groups
							.findFrequenciesByGene(filtedFrequencies, candidate);
					if (filtedFrequencies.size() == afterFiltedFrequencies
							.size()) {
						combinedGene.addGene(candidate);
						candidate.setCombined(true);
					}
				}
				if (combinedGene.getGenes().size() > 1) {
					// System.out.println("在第" + (i + 1) + "组得到一个大小为"
					// + combinedGene.getGenes().size() + "的可合并基因组!");
					// System.out.println("当前得到");
					// for (int x = 0; x < combinedGene.getGenes().size(); x++)
					// {
					// System.out.print(combinedGene.getGenes().get(x)
					// .getName()
					// + " , ");
					// }
					// System.out.println("可以合并为一个基因组！");
					combines.add(combinedGene);
				} else {
					current.setCombined(false);
				}
			}
		}
		int count = 0;
		for (int i = 0; i < combines.size(); i++) {
			count += combines.get(i).getGenes().size();
		}
		System.out.println("共有" + combines.size() + "组合并的基因，也就是基因数目从"
				+ Statistics.GENES_NUM + "个合并到"
				+ (Statistics.GENES_NUM - count + combines.size()) + "个");
		return combines;
	}

	public double[] calculateCombinedGARelation() {
		Group groups = getGARelation();
		int[] count_old = new int[Statistics.ADRS_NUM];
		int[] count_combined = new int[Statistics.ADRS_NUM];
		List<CombinedGene> combines = getCombinedGARalation(groups);
		Map<String, Integer> adrs_encode = statistics.getAdrs_encode();
		for (int i = 0; i < groups.getFreq().size(); i++) {
			List<Frequencies> freq = groups.getFreq();
			List<Gene> adr2genes = freq.get(i).getGroup();
			for (int j = 0; j < adr2genes.size(); j++) {
				count_old[adrs_encode.get(freq.get(i).getAdr().getName())]++;
			}
		}
		count_combined = count_old.clone();
		List<Gene> judgement = getJudgement(combines);
		if (judgement == null) {
			return statiticsCount(count_old, 40, 100);
		}
		for (int i = 0; i < groups.getFreq().size(); i++) {
			Frequencies current = groups.getFreq().get(i);
			// 获得该副作用-基因对应关系的基因组内存在哪几种可合并组合
			int[] indexes = current.index(judgement);
			if (indexes.length != 0) {// 表示存在可合并组合
				int count = 0;
				for (int j = 0; j < indexes.length; j++) {
					count += combines.get(indexes[j]).getGenes().size();
				}
				int x = adrs_encode.get(current.getAdr().getName());
				count_combined[x] = count_old[x] - count + indexes.length;
			}
		}
		return statiticsCount(count_combined, 40, 100);
	}

	/**
	 * 从合并基因组里面每一个组拿一个基因出来作为判别基因组<br>
	 * 用于判断每个副作用对应基因组里面存在哪一种可合并的基因组合
	 * 
	 * @param combines
	 * @return
	 */
	private List<Gene> getJudgement(List<CombinedGene> combines) {
		if (combines.size() == 0) {
			return null;
		}
		System.out.println("可合并基因组：");
		for (int i = 0; i < combines.size(); i++) {
			List<Gene> g = combines.get(i).getGenes();
			System.out.print("第" + (i + 1) + "组：");
			for (int j = 0; j < g.size(); j++) {
				System.out.print(g.get(j).getName() + ",");
			}
			System.out.println();
		}
		List<Gene> judgement = new ArrayList<Gene>();
		for (int i = 0; i < combines.size(); i++) {
			judgement.add(combines.get(i).getGenes().get(0));
		}
		return judgement;
	}

	private void printRelation(double[] results) {
		String[] labels = { "总ADR数", "对应基因不多于40的ADR数", "对应基因不多于100的ADR数",
				"对应基因最小值", "对应基因最大值", "对应基因平均值", "对应基因标准差" };
		for (int i = 0; i < labels.length; i++) {
			System.out.println(labels[i] + " : " + results[i]);
		}
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		DataTesting test = new DataTesting();
		long start = System.currentTimeMillis();
		// test.printRelation(test.calculateGARelation());
		test.printRelation(test.calculateCombinedGARelation());
		long end = System.currentTimeMillis();
		System.out.println("运行完成，总共耗时" + Tool.formatTimeString(end - start));
	}
}
